use std::time::Duration;

use serde::{Deserialize, Serialize};

use crate as honeybee;
use crate::etcd;
use crate::prelude::message;
use crate::prelude::*;
use crate::Result;

#[derive(Serialize, Deserialize, Debug)]
pub struct UscServerInfo {
    pub node_name: String,
    pub node_total_number: usize,
    pub node_endpoint: String,
}

pub struct EtcdUscServerActor {
    addr: Option<Addr<Self>>,
    etcd_client: etcd_client::Client,
    node_name: String,
    node_total_number: usize,
    node_endpoint: String,

    lease_keeper: Option<etcd_client::LeaseKeeper>,
}

impl EtcdUscServerActor {
    pub fn new(node_name: &str, node_total_number: usize, node_endpoint: String) -> Result<Self> {
        let this = Self {
            addr: None,
            etcd_client: etcd::get_client(),
            node_name: node_name.into(),
            node_total_number,
            node_endpoint,

            lease_keeper: None,
        };
        Ok(this)
    }
}

#[async_trait::async_trait]
impl Actor for EtcdUscServerActor {
    fn name(&self) -> &str {
        "EtcdUscServerActor"
    }

    async fn started(&mut self, ctx: &mut Context<Self>) -> Result<()> {
        info!("etcd usc server actor:{} started", self.name());
        self.addr = Some(ctx.address());

        let lease = self.etcd_client.lease_grant(10, None).await?;
        let (lease_keeper, _) = self.etcd_client.lease_keep_alive(lease.id()).await?;
        self.lease_keeper = Some(lease_keeper);

        let put_opt = etcd_client::PutOptions::new().with_lease(lease.id());
        let info = UscServerInfo {
            node_name: self.node_name.clone(),
            node_total_number: self.node_total_number,
            node_endpoint: self.node_endpoint.clone(),
        };
        self.etcd_client
            .put(
                format!("/usc/{}", self.node_name),
                serde_json::to_string(&info)?,
                Some(put_opt),
            )
            .await?;

        ctx.send_interval(LeaseKeepAlive, Duration::from_secs(5));

        ctx.register_shutdown().await?;

        Ok(())
    }

    async fn stopped(&mut self, ctx: &mut Context<Self>) {
        let _ = ctx.unregister_shutdown().await;
        if let Some(ref mut lease_keeper) = self.lease_keeper {
            let _ = self.etcd_client.lease_revoke(lease_keeper.id()).await;
        }
        info!("{} stoped", self.name());
    }
}

#[message]
#[derive(Clone)]
pub struct LeaseKeepAlive;
#[async_trait::async_trait]
impl Handler<LeaseKeepAlive> for EtcdUscServerActor {
    async fn handle(&mut self, _ctx: &mut Context<Self>, _: LeaseKeepAlive) -> Result<()> {
        if let Some(ref mut lease_keeper) = self.lease_keeper {
            if let Err(err) = lease_keeper.keep_alive().await {
                error!(
                    "node:{} lease keep alive failed err:{}",
                    self.node_name, err
                );
            }
            //debug!("lease keep alive");
        }
        Ok(())
    }
}
